/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.gwt.testing.easygwtmock.client.internal;

import com.google.gwt.testing.easygwtmock.client.Answer;
import com.google.gwt.testing.easygwtmock.client.ArgumentMatcher;
import com.google.gwt.testing.easygwtmock.client.Capture;
import com.google.gwt.testing.easygwtmock.client.ExpectationSetters;
import com.google.gwt.testing.easygwtmock.client.MocksControl;
import com.google.gwt.testing.easygwtmock.client.internal.matchers.Any;
import com.google.gwt.testing.easygwtmock.client.internal.matchers.ArgumentCapture;
import com.google.gwt.testing.easygwtmock.client.internal.matchers.AsyncCallbackMatcher;
import com.google.gwt.testing.easygwtmock.client.internal.matchers.Equals;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Abstract base class for the MocksControl class generated by
 * {@link com.google.gwt.testing.easygwtmock.rebind.MocksControlGenerator}. Provides
 * functionality that is the same for every MocksControl and therefore does not need to be
 * generated.
 * 
 * @author Michael Goderbauer
 */
public abstract class MocksControlBase implements MocksControl {
  
  private MocksControlState currentState;
  private MocksBehavior behavior;
  
  public MocksControlBase() {
    reset();
  }
  
  public Answer<? extends Object> invoke(Call call) throws AssertionErrorWrapper {
    return this.currentState.invoke(call);
  }
  
  public void unmockableCallTo(String methodName) {
    this.currentState.unmockableCallTo(methodName);
  }
  
  @Override
  public void replay() {
    try {
      this.currentState.checkCanSwitchToReplay();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    this.currentState = new ReplayState(behavior);
  }

  @Override
  public void verify() {
    try {
      this.currentState.verify();
    } catch (AssertionErrorWrapper e) {
      throw (AssertionError) e.getAssertionError().fillInStackTrace();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
  }
  
  @Override
  public void reset() {
    this.behavior = new MocksBehavior();
    this.currentState = new RecordState(this.behavior);
  }
  
  @Override
  @SuppressWarnings("unchecked")
  public <T> ExpectationSetters<T> expect(final T value) {
    try {
      return (ExpectationSetters<T>) this.currentState.getExpectationSetter();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
  }
  
  @Override
  @SuppressWarnings("unchecked")
  public <T> ExpectationSetters<T> expectLastCall() {
    try {
      return (ExpectationSetters<T>) this.currentState.getExpectationSetter();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
  }
  
  @Override
  public <T> T setToNice(T mock) {
    this.behavior.addNiceMock(mock);
    return mock;
  }
  
  @Override
  public <T> T setToNotNice(T mock) {
    this.behavior.removeNiceMock(mock);
    return mock;
  }
  
  /*
   * Argument Matcher
   */
  
  private void reportAnyMatcher() throws IllegalStateExceptionWrapper {
    this.currentState.reportMatcher(Any.ANY);
  }
  
  @Override
  public boolean anyBoolean() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return false;
  }

  @Override
  public byte anyByte() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public char anyChar() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public int anyInt() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public long anyLong() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public float anyFloat() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public double anyDouble() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public short anyShort() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public <T> T anyObject() {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }

  @Override
  public <T> T anyObject(final Class<T> clazz) {
    try {
      reportAnyMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }
  
  private void reportEqualMatcher(Object value) throws IllegalStateExceptionWrapper {
    this.currentState.reportMatcher(new Equals(value));
  }

  @Override
  public boolean eq(boolean value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return false;
  }

  @Override
  public byte eq(byte value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public char eq(char value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public double eq(double value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public float eq(float value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public int eq(int value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public long eq(long value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public short eq(short value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public <T> T eq(T value) {
    try {
      reportEqualMatcher(value);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return value;
  }
  
  private <T> void reportCapture(Capture<T> capture) throws IllegalStateExceptionWrapper {
    this.currentState.reportCapture(new ArgumentCapture(capture));
  }
  
  @Override
  public <T> T captureObject(Capture<T> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }

  @Override
  public byte captureByte(Capture<Byte> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public short captureShort(Capture<Short> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public int captureInt(Capture<Integer> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public long captureLong(Capture<Long> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public float captureFloat(Capture<Float> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public double captureDouble(Capture<Double> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public boolean captureBoolean(Capture<Boolean> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return false;
  }

  @Override
  public char captureChar(Capture<Character> capture) {
    try {
      reportCapture(capture);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public <T> T matchesObject(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }
  
  @Override
  public <T> T matchesObject(ArgumentMatcher matcher, Class<T> clazz) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }
  
  @Override
  public byte matchesByte(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public short matchesShort(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public int matchesInt(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public long matchesLong(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public float matchesFloat(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public double matchesDouble(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }

  @Override
  public boolean matchesBoolean(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return false;
  }

  @Override
  public char matchesChar(ArgumentMatcher matcher) {
    try {
      this.currentState.reportMatcher(matcher);
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return 0;
  }
  
  private void reportAsyncCallbackMatcher() throws IllegalStateExceptionWrapper {
    this.currentState.reportMatcher(AsyncCallbackMatcher.MATCHER);
  }

  @Override
  public <T> AsyncCallback<T> asyncCallback() {
    try {
      reportAsyncCallbackMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }

  @Override
  public <T> AsyncCallback<T> asyncCallback(Class<T> clazz) {
    try {
      reportAsyncCallbackMatcher();
    } catch (IllegalStateExceptionWrapper e) {
      throw (IllegalStateException) e.getIllegalStateException().fillInStackTrace();
    }
    return null;
  }
}
